package com.bright.ghj.overt.manager.impl;

import com.bright.ghj.common.pojo.dto.PageDTO;
import com.bright.ghj.common.util.StringUtil;
import com.bright.ghj.overt.constant.DataConstant;
import com.bright.ghj.overt.constant.PublicConstant;
import com.bright.ghj.overt.converter.OpenReportRecordConverter;
import com.bright.ghj.overt.enums.DataStatus;
import com.bright.ghj.overt.manager.FileListManager;
import com.bright.ghj.overt.manager.OpenReportRecordManager;
import com.bright.ghj.overt.pojo.dto.FileListDTO;
import com.bright.ghj.overt.pojo.dto.OpenReportRecordDTO;
import com.bright.ghj.overt.pojo.po.primary.OpenReportRecord;
import com.bright.ghj.overt.pojo.query.OpenReportRecordQuery;
import com.bright.ghj.overt.repository.primary.OpenReportRecordRepository;
import com.bright.ghj.overt.util.EntityManagerUtil;
import com.bright.ghj.common.util.ListUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

/**
 * @Author hxj
 * @Date 2023/12/26 12:00
 * @Description
 */
@Component
@RequiredArgsConstructor
public class OpenReportRecordManagerImpl implements OpenReportRecordManager {

    private final OpenReportRecordConverter openReportRecordConverter;
    private final OpenReportRecordRepository openReportRecordRepository;

    private final DataConstant dataConstant;

    @PersistenceContext
    private final EntityManager entityManager;

    private final FileListManager fileListManager;

    @Override
    public OpenReportRecordDTO getById(Integer id) {
        if (id == null) {
            throw new RuntimeException("id不能为空！");
        }
        Optional<OpenReportRecord> openReportRecord = openReportRecordRepository.findById(id);
        if (!openReportRecord.isPresent()) {
            throw new RuntimeException("id没有对应的openReportRecord数据!" + id);
        }
        return openReportRecordConverter.poConvertDTO(openReportRecord.get());
    }

    @Override
    public List<OpenReportRecordDTO> listById(List<Integer> ids) {
        List<OpenReportRecord> allById = openReportRecordRepository.findAllById(ids);
        return openReportRecordConverter.poConvertDTO(allById);
    }

    @Override
    public void save(OpenReportRecordDTO openReportRecordDTO) {
        OpenReportRecord openReportRecord = openReportRecordConverter.dtoConvertPO(openReportRecordDTO);
        openReportRecordRepository.save(openReportRecord);
    }

    @Override
    public void saveAll(List<OpenReportRecordDTO> openReportRecordDTOS) {
        List<OpenReportRecord> list = openReportRecordConverter.dtoConvertPO(openReportRecordDTOS);
        openReportRecordRepository.saveAll(list);
    }

    @Override
    public void deleteAll(List<OpenReportRecordDTO> openReportRecordDTOS) {
        List<OpenReportRecord> list = openReportRecordConverter.dtoConvertPO(openReportRecordDTOS);
        openReportRecordRepository.deleteAll(list);
    }

    @Override
    public PageDTO<OpenReportRecordDTO> pageByCondition(OpenReportRecordQuery query, int listType, Pageable pageable) {
        Page<OpenReportRecord> page = openReportRecordRepository.findAll(getPredicate(query, listType), pageable);
        List<OpenReportRecordDTO> list = openReportRecordConverter.poConvertDTO(page.getContent());
        return PageDTO.of(page.getTotalElements(), page.getTotalPages(), list);
    }

    @Override
    public List<OpenReportRecordDTO> listByCondition(OpenReportRecordQuery query, int listType) {
        List<OpenReportRecord> list = openReportRecordRepository.findAll(getPredicate(query, listType));
        return openReportRecordConverter.poConvertDTO(list);
    }

    private Specification<OpenReportRecord> getPredicate(OpenReportRecordQuery query, int listType) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> ps = new ArrayList<>();
            if (query.getYears() != null && query.getYears() != 0) {
                ps.add(criteriaBuilder.equal(root.get("years"), query.getYears()));
            }
            if (query.getMonths() != null && query.getMonths() != 0) {
                ps.add(criteriaBuilder.equal(root.get("months"), query.getMonths()));
            }
            // 地区号和账套号只取其中一个作为条件 优先顺序是ztId > ztIds > distNo
            if (StringUtil.isNotEmpty(query.getZtId())) {
                ps.add(criteriaBuilder.equal(root.get("ztId"), query.getZtId()));
            } else if (query.getZtIds() != null && query.getZtIds().size() > 0) {
                ps.add(root.get("ztId").in(query.getZtIds()));
            } else if (StringUtil.isNotEmpty(query.getDistNo()) && !StringUtil.contains(query.getDistNo(), ",")) {
                ps.add(criteriaBuilder.like(root.get("distNo"), query.getDistNo() + "%"));
            } else {
                // 用userDistNos限制 and (distNo like ?% or distNo like ?% ...)
                Predicate[] predicates = new Predicate[query.getUserDistNos().length];
                int count = 0;
                for (String userDistNo : query.getUserDistNos()) {
                    predicates[count] = criteriaBuilder.like(root.get("distNo"), userDistNo + "%");
                    count++;
                }
                if (predicates.length > 0) {
                    ps.add(criteriaBuilder.or(predicates));
                }

            }
            if (StringUtil.isNotEmpty(query.getTableName())) {
                ps.add(criteriaBuilder.equal(root.get("tableName"), query.getTableName()));
            }
            // sno
            if (StringUtil.isNotEmpty(query.getSno())) {
                ps.add(criteriaBuilder.equal(root.get("sno"), query.getSno()));
            }
            // status
            addStatusPredicate(ps, criteriaBuilder, root, query, listType);
            // category 财务 党务 事务 若不传则默认查财务
            String category;
            if (query.getYwType() == PublicConstant.GK_TYPE_DW) {
                category = dataConstant.DW_CATEGORY;
            } else if (query.getYwType() == PublicConstant.GK_TYPE_SW) {
                category = dataConstant.SW_CATEGORY;
            } else {
                category = dataConstant.CW_CATEGORY;
            }
            ps.add(criteriaBuilder.equal(root.get("category"), category));

            // 筛选已审核数据、未审核数据
            Boolean isAudit = query.getIsAudit();
            if (isAudit != null) {
                Path<Boolean> chkFlag = root.get("chkFlag");
                if (isAudit) {
                    ps.add(criteriaBuilder.isTrue(chkFlag));
                } else {
                    ps.add(criteriaBuilder.or(criteriaBuilder.isNull(chkFlag), criteriaBuilder.isFalse(chkFlag)));
                }
            }
            // 是否审核通过、审核不通过
            Boolean isAuditOK = query.getIsAuditOK();
            if (isAuditOK != null) {
                Path<Boolean> okFlag = root.get("okFlag");
                if (isAuditOK) {
                    ps.add(criteriaBuilder.isTrue(okFlag));
                } else {
                    ps.add(criteriaBuilder.or(criteriaBuilder.isNull(okFlag), criteriaBuilder.isFalse(okFlag)));
                }
            }

            // 是否已发布
            Boolean isPublish = query.getIsPublish();
            if (isPublish != null) {
                Path<Boolean> isUpload = root.get("isUpload");
                if (isPublish) {
                    ps.add(criteriaBuilder.isTrue(isUpload));
                } else {
                    ps.add(criteriaBuilder.or(criteriaBuilder.isNull(isUpload), criteriaBuilder.isFalse(isUpload)));
                }
            }

            // publicDate起止（旧数据没有生成规定公开时间 先不写这个）
            // uploadDate起止
            if (query.getUploadDateBegin() != null) {
                ps.add(criteriaBuilder.greaterThanOrEqualTo(root.get("uploadDate"), query.getUploadDateBegin()));
            }
            if (query.getUploadDateEnd() != null) {
                ps.add(criteriaBuilder.lessThanOrEqualTo(root.get("uploadDate"), query.getUploadDateEnd()));
            }

            // 20240430 按用户所属地区或账套限制能查看的数据
            String[] userDistNos = query.getUserDistNos();
            if (userDistNos != null) {
                List<Predicate> psOr = new ArrayList<>();
                for (String userDistNo : userDistNos) {
                    psOr.add(criteriaBuilder.like(root.get("distNo"), userDistNo + "%"));
                }
                if (psOr.size() == 1) {
                    ps.add(psOr.get(0));
                } else if (psOr.size() > 1) {
                    ps.add(criteriaBuilder.or(psOr.toArray(new Predicate[0])));
                }
            }
            String[] userZtIds = query.getUserZtIds();
            if (userZtIds != null) {
                List<Predicate> psOr = new ArrayList<>();
                for (String userZtId : userZtIds) {
                    psOr.add(criteriaBuilder.equal(root.get("ztId"), userZtId));
                }
                if (psOr.size() == 1) {
                    ps.add(psOr.get(0));
                } else if (psOr.size() > 1) {
                    ps.add(criteriaBuilder.or(psOr.toArray(new Predicate[0])));
                }
            }

            criteriaQuery.where(ps.toArray(new Predicate[0]));
            criteriaQuery.orderBy(criteriaBuilder.asc(root.get("id")));
            return criteriaQuery.getRestriction();
        };
    }

    // 增加状态查询条件
    private void addStatusPredicate(List<Predicate> ps, CriteriaBuilder criteriaBuilder, Root<OpenReportRecord> root, OpenReportRecordQuery query, int listType) {
        // 录入、审核或发布的状态
        // 20240223 取消“报审”操作 审核列表也显示全部
//        if (listType == PublicConstant.LIST_TYPE_AUDIT) {
//            // 审核列表 status处于审核等状态
//            List<DataStatus> statusList = new ArrayList<>();
//            statusList.add(DataStatus.READY_AUDIT);
//            statusList.add(DataStatus.AUDIT_SUCCESS_AND_READY_PUBLISH);
//            statusList.add(DataStatus.AUDIT_FAIL);
//            List<Integer> statusNoList = ListUtil.toSingleList(statusList, DataStatus::getStatus);
//            ps.add(root.get("status").in(statusNoList));
//        } else
        if (listType == PublicConstant.LIST_TYPE_PUBLISH) {
            // 发布列表 status处于发布等状态
            List<DataStatus> statusList = new ArrayList<>();
            statusList.add(DataStatus.AUDIT_SUCCESS_AND_READY_PUBLISH);
//            statusList.add(DataStatus.PUBLISHED_AND_READY_CITY_UPLOAD);
            statusList.add(DataStatus.PUBLISHED);
            List<Integer> statusNoList = ListUtil.toSingleList(statusList, DataStatus::getStatus);
            ps.add(root.get("status").in(statusNoList));
        }
        // 录入、审核 所有状态 不加限制

        // 再根据传进来的筛选状态构建查询条件
//        int[] statusArray = query.getStatus();
//        if (statusArray != null && statusArray.length > 0) {
//            ps.add(root.get("status").in(Arrays.asList(statusArray)));
//        }
        // 改成只传单一一个状态了
        Integer status = query.getStatus();
        if (status != null) {
            ps.add(criteriaBuilder.equal(root.get("status"), status));
        }
    }

    @Override
    public boolean isAudit(OpenReportRecordDTO openReportRecord) {
        return (openReportRecord.getChkFlag() != null && openReportRecord.getChkFlag());
    }

    @Override
    public boolean isAuditOK(OpenReportRecordDTO openReportRecord) {
        return (openReportRecord.getChkFlag() != null && openReportRecord.getChkFlag()) &&
                (openReportRecord.getOkFlag() != null && openReportRecord.getOkFlag());
    }

    @Override
    public boolean isPublished(OpenReportRecordDTO openReportRecord) {
        return openReportRecord.getIsUpload() != null && openReportRecord.getIsUpload();
    }

    @Override
    public boolean isEditEnable(OpenReportRecordDTO openReportRecord) {
        // 已审核通过的不允许编辑
        boolean isNotAuditOk = !isAuditOK(openReportRecord);
        if (!isNotAuditOk) {
            return false;
        }
        // v1.0.2 设置为不可编辑的公开表
        FileListDTO fileListByCache = fileListManager.getFileListByCache(openReportRecord.getTableName());
        return !fileListByCache.getCanNotEdit();
    }

    @Override
    public void setReadyAuditStatus(OpenReportRecordDTO openReportRecord) {
        // 设置成未审核
        openReportRecord.setChkFlag(false);

        setDataStatus(openReportRecord, DataStatus.READY_AUDIT);
    }

    @Override
    public void setAuditSuccessAndReadyPublishStatus(OpenReportRecordDTO openReportRecord, String operator) {
        // 设置已审核标识
        openReportRecord.setChkFlag(true);
//        openReportRecord.setChkDate(new Date());
//        openReportRecord.setChkUser(userName);
        // 设置审核通过标识
        openReportRecord.setOkFlag(true);
        openReportRecord.setOkDate(new Date());

        // 取消发布也是调用这步
        openReportRecord.setIsUpload(false);
        openReportRecord.setUploadDate(null);
        openReportRecord.setUploadUser(null);

        setDataStatus(openReportRecord, DataStatus.AUDIT_SUCCESS_AND_READY_PUBLISH);
    }

    @Override
    public void setAuditFailStatus(OpenReportRecordDTO openReportRecord, String operator) {
        // 设置审核不通过标识
        openReportRecord.setChkFlag(true);
//        openReportRecord.setChkDate(new Date());
//        openReportRecord.setChkUser(operator);
        // 设置审核不通过标识
        openReportRecord.setOkFlag(false);
        openReportRecord.setOkDate(null);

        setDataStatus(openReportRecord, DataStatus.AUDIT_FAIL);
    }

    @Override
    @Deprecated
    public void setPublishedAndReadyCityUploadStatus(OpenReportRecordDTO openReportRecord, String operator) {
//        // 设置已发布标识
//        openReportRecord.setIsUpload(true);
//        openReportRecord.setUploadDate(new Date());
//        openReportRecord.setUploadUser(operator);

        setDataStatus(openReportRecord, DataStatus.PUBLISHED_AND_READY_CITY_UPLOAD);
    }

    @Override
    public void setPublishedStatus(OpenReportRecordDTO openReportRecord, String operator) {
        // 设置已发布标识
        openReportRecord.setIsUpload(true);
        openReportRecord.setUploadDate(new Date());
        openReportRecord.setUploadUser(operator);

        setDataStatus(openReportRecord, DataStatus.PUBLISHED);
    }

    @Override
    public void setReturnToStartStatus(OpenReportRecordDTO openReportRecord, String operator) {
        openReportRecord.setIsUpload(false);
        openReportRecord.setUploadUser(null);
        openReportRecord.setUploadDate(null);
        openReportRecord.setChkFlag(false);
        openReportRecord.setOkFlag(false);
        openReportRecord.setOkDate(null);

        setDataStatus(openReportRecord, DataStatus.WAIT_FOR_PROCESS);
    }

    @Override
    public Map<String, Object> getFieldValueByFieldNames(Integer id, List<String> fieldNames) {
//        Query nativeQuery = entityManager.createNativeQuery("select " + StringUtil.join(fieldNames.toArray(), ",") + " from openReportRecord where id=:id");
//        nativeQuery.setParameter("id", id);
//        nativeQuery.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
//        return (Map<String, Object>) nativeQuery.getSingleResult();
        String querySQL = "select " + StringUtil.join(fieldNames.toArray(), ",") + " from openReportRecord where id=:id";
        Map<String, Object> params = new HashMap<>();
        params.put("id", id);
        List<Map<String, Object>> maps = EntityManagerUtil.queryForMapList(entityManager, querySQL, params);
        if (maps.size() == 0) {
            return new HashMap<>();
        }
        return maps.get(0);
    }

    @Transactional
    @Override
    public void updateParamsById(Integer id, Map<String, Object> params) {
        if (params.size() == 0) return;

        StringBuilder updateSQL = new StringBuilder("update openReportRecord set ");
        List<String> sqlList = ListUtil.toSingleList(new ArrayList<>(params.keySet()), key -> key + "=:" + key);
        updateSQL.append(StringUtil.join(sqlList, ",")).append(" where id=:id");

        params.put("id", id);
        EntityManagerUtil.update(entityManager, updateSQL.toString(), params);
    }


    private void setDataStatus(OpenReportRecordDTO openReportRecordDTO, DataStatus status) {
        openReportRecordDTO.setStatus(status.getStatus());
        openReportRecordDTO.setStatusFlag(status.getStatusFlag());
//        save(openReportRecordDTO);
    }
}
